import { ref, reactive } from 'vue'

export interface CacheItem<T = any> {
  data: T
  timestamp: number
  expiry?: number
}

export interface CacheOptions {
  ttl?: number // Time to live in milliseconds
  storage?: 'memory' | 'localStorage' | 'sessionStorage'
  prefix?: string
}

export function useCache(defaultOptions: CacheOptions = {}) {
  const memoryCache = reactive<Map<string, CacheItem>>(new Map())
  const defaultTTL = defaultOptions.ttl || 5 * 60 * 1000 // 5 minutes
  const storageType = defaultOptions.storage || 'memory'
  const prefix = defaultOptions.prefix || 'app_cache_'

  const getStorageKey = (key: string) => `${prefix}${key}`

  const isExpired = (item: CacheItem): boolean => {
    if (!item.expiry) return false
    return Date.now() > item.expiry
  }

  const setMemoryCache = <T>(key: string, data: T, ttl?: number): void => {
    const expiry = ttl ? Date.now() + ttl : undefined
    memoryCache.set(key, {
      data,
      timestamp: Date.now(),
      expiry
    })
  }

  const getMemoryCache = <T>(key: string): T | null => {
    const item = memoryCache.get(key)
    if (!item) return null
    
    if (isExpired(item)) {
      memoryCache.delete(key)
      return null
    }
    
    return item.data as T
  }

  const setStorageCache = <T>(
    key: string, 
    data: T, 
    storage: Storage, 
    ttl?: number
  ): void => {
    try {
      const expiry = ttl ? Date.now() + ttl : undefined
      const cacheItem: CacheItem<T> = {
        data,
        timestamp: Date.now(),
        expiry
      }
      storage.setItem(getStorageKey(key), JSON.stringify(cacheItem))
    } catch (error) {
      console.warn('Failed to set storage cache:', error)
    }
  }

  const getStorageCache = <T>(key: string, storage: Storage): T | null => {
    try {
      const item = storage.getItem(getStorageKey(key))
      if (!item) return null
      
      const cacheItem: CacheItem<T> = JSON.parse(item)
      
      if (isExpired(cacheItem)) {
        storage.removeItem(getStorageKey(key))
        return null
      }
      
      return cacheItem.data
    } catch (error) {
      console.warn('Failed to get storage cache:', error)
      return null
    }
  }

  const set = <T>(key: string, data: T, options?: CacheOptions): void => {
    const opts = { ...defaultOptions, ...options }
    const ttl = opts.ttl || defaultTTL

    switch (opts.storage) {
      case 'localStorage':
        setStorageCache(key, data, localStorage, ttl)
        break
      case 'sessionStorage':
        setStorageCache(key, data, sessionStorage, ttl)
        break
      default:
        setMemoryCache(key, data, ttl)
    }
  }

  const get = <T>(key: string, options?: CacheOptions): T | null => {
    const opts = { ...defaultOptions, ...options }

    switch (opts.storage) {
      case 'localStorage':
        return getStorageCache<T>(key, localStorage)
      case 'sessionStorage':
        return getStorageCache<T>(key, sessionStorage)
      default:
        return getMemoryCache<T>(key)
    }
  }

  const remove = (key: string, options?: CacheOptions): void => {
    const opts = { ...defaultOptions, ...options }

    switch (opts.storage) {
      case 'localStorage':
        localStorage.removeItem(getStorageKey(key))
        break
      case 'sessionStorage':
        sessionStorage.removeItem(getStorageKey(key))
        break
      default:
        memoryCache.delete(key)
    }
  }

  const clear = (options?: CacheOptions): void => {
    const opts = { ...defaultOptions, ...options }

    switch (opts.storage) {
      case 'localStorage':
        Object.keys(localStorage).forEach(key => {
          if (key && key.startsWith(prefix)) {
            localStorage.removeItem(key)
          }
        })
        break
      case 'sessionStorage':
        Object.keys(sessionStorage).forEach(key => {
          if (key && key.startsWith(prefix)) {
            sessionStorage.removeItem(key)
          }
        })
        break
      default:
        memoryCache.clear()
    }
  }

  const has = (key: string, options?: CacheOptions): boolean => {
    return get(key, options) !== null
  }

  const size = (options?: CacheOptions): number => {
    const opts = { ...defaultOptions, ...options }

    switch (opts.storage) {
      case 'localStorage':
        return Object.keys(localStorage).filter(key => 
          key && key.startsWith(prefix)
        ).length
      case 'sessionStorage':
        return Object.keys(sessionStorage).filter(key => 
          key && key.startsWith(prefix)
        ).length
      default:
        return memoryCache.size
    }
  }

  const keys = (options?: CacheOptions): string[] => {
    const opts = { ...defaultOptions, ...options }

    switch (opts.storage) {
      case 'localStorage':
        return Object.keys(localStorage)
          .filter(key => key && key.startsWith(prefix))
          .map(key => key.replace(prefix, ''))
      case 'sessionStorage':
        return Object.keys(sessionStorage)
          .filter(key => key && key.startsWith(prefix))
          .map(key => key.replace(prefix, ''))
      default:
        return Array.from(memoryCache.keys())
    }
  }

  // 缓存装饰器，用于缓存函数结果
  const cached = <T extends (...args: any[]) => Promise<any>>(
    fn: T,
    keyGenerator?: (...args: Parameters<T>) => string,
    options?: CacheOptions
  ) => {
    return async (...args: Parameters<T>): Promise<Awaited<ReturnType<T>>> => {
      const cacheKey = keyGenerator 
        ? keyGenerator(...args)
        : `fn_${fn.name}_${JSON.stringify(args)}`
      
      const cached = get<Awaited<ReturnType<T>>>(cacheKey, options)
      if (cached !== null) {
        return cached
      }

      const result = await fn(...args)
      set(cacheKey, result, options)
      return result
    }
  }

  // 清理过期缓存
  const cleanup = (): void => {
    // 清理内存缓存
    for (const [key, item] of memoryCache.entries()) {
      if (isExpired(item)) {
        memoryCache.delete(key)
      }
    }

    // 清理 localStorage
    Object.keys(localStorage).forEach(key => {
      if (key && key.startsWith(prefix)) {
        try {
          const item = JSON.parse(localStorage.getItem(key) || '{}')
          if (isExpired(item)) {
            localStorage.removeItem(key)
          }
        } catch (error) {
          // 如果解析失败，删除该项
          localStorage.removeItem(key)
        }
      }
    })

    // 清理 sessionStorage
    Object.keys(sessionStorage).forEach(key => {
      if (key && key.startsWith(prefix)) {
        try {
          const item = JSON.parse(sessionStorage.getItem(key) || '{}')
          if (isExpired(item)) {
            sessionStorage.removeItem(key)
          }
        } catch (error) {
          sessionStorage.removeItem(key)
        }
      }
    })
  }

  return {
    set,
    get,
    remove,
    clear,
    has,
    size,
    keys,
    cached,
    cleanup
  }
}